前几天在某公司面试的时候被问到关于这个方法的默认值的问题(然而面试官跟我说的其实是错的,当场我还不够底气去反驳)。突然发现对这个方法的了解还不够,因此回来查了资料,看了v8引擎的实现和ECMA标准,在这分享一下我的总结。
先给几个结论把,然后我们再从ECMA标准去看这个方法。
- sort方法会修改原本数组。
- sort方法是不稳定的。
- sort方法可以接受一个可选的参数,comparefn(比较回调函数)。
- sort方法始终是默认升序的。
1.sort方法会修改原本数组
let a = [1, 20, 13, 110]
a.sort()
console.log(a) // 输出[1, 110, 13, 20]
如上,a在调用sort方法后,自身数组被修改。
2.sort方法是不稳定的
这句话具体来说应该是,sort方法在长数组排序的时候是不稳定的。在v8引擎的里,对于短数组会使用插入排序,而插入排序是稳定的。对于长数组会使用快速排序,而快速排序一般是不稳定的。具体可以读v8引擎的代码,v8引擎内的数组方法实现,从710行开始。
3.sort方法可以接受一个可选的参数,ccomparefn(比较回调函数)。
我们来看一下v8引擎中关于这一部分的代码
if (!IS_CALLABLE(comparefn)) { comparefn = function (x, y) { if (x === y) return 0; if (%_IsSmi(x) && %_IsSmi(y)) { return %SmiLexicographicCompare(x, y); } x = TO_STRING(x); y = TO_STRING(y); if (x == y) return 0; else return x < y ? -1 : 1; }; }
可以看出,在不传递comparefn这个参数的情况下,默认会使用转换为的字符串的诸个字符的Unicode位点进行排序。另外值得注意的一点是,undefined在默认情况下总是会被排在数组的最后,这一点可以参考ECMA-262标准中关于Array.prototype.sort(comparefn)的描述。
When the SortCompare operatoris called with two arguments x and y, the
following steps are taken:
- If x and y are both undefined, return +0.
- If x is undefined, return 1.
- If y is undefined, return −1.
- If the argument comparefn was not provided in the call to sort, go to step 7.
- Call comparefn with arguments x and y.
- Return Result(5).
- Call ToString(x).
- Call ToString(y).
- If Result(7) < Result(8), return −1.
- If Result(7) > Result(8), return 1.
- Return +0.
以下是我简单翻译的规则:
当比较操作函数用x,y两个参数调用的时候,会按照接下来的步骤进行:
- 如果x,y都是undefined,返回 +0
- 如果x是undefined,返回 1
- 如果y是undefined,返回 -1
- 如果没有提供comparefn,跳至第七步
- 用comparefn比较x,y
- 返回第五步的比较结果
- x.toString()
- y.toString()
- 如果第八步大于第七步,返回-1
- 如果第七步大于第八步,返回1
- 返回+0
因此[1, 20, 2, 10].sort()的结果会是[1, 10, 2, 20],而不是预期的[1, 2, 10, 20]。
4. sort方法始终是默认升序的。
另外关于如何根据x,y比较结果进行排序,有以下规则:
- 如果 comparefn(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
- 如果 comparefn(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本, 例如v8引擎, 这一点其实就是上面所说的排序不稳定);
- 如果 comparefn(a, b) 大于 0 , b 会被排列到 a 之前。
所以sort方法返回的数组永远是该方法认为的升序数组。我们要做的事情就是令我们想要放在后面的数大于放在前面的数。
比如我们想要返回一个降序的数字数组传入的comparefn应该是 (a, b) => b - a
参考资料:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。